package com.xucia.jsponic.job;

import java.io.File;
import java.io.FileInputStream;
import java.io.FilenameFilter;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.mozilla.javascript.NativeObject;
import org.mozilla.javascript.Scriptable;

import com.xucia.jsponic.JsponicRuntime;
import com.xucia.jsponic.data.DataSourceManager;
import com.xucia.jsponic.data.GlobalData;
import com.xucia.jsponic.data.Persistable;
import com.xucia.jsponic.data.PersistableList;

public class ImportUpgradeData implements Job{
	Persistable importMap;
	public void execute() {
		Persistable importMaps;
		Object importObject = DataSourceManager.getRootObject().get(GlobalData.IMPORT_MAPS);		
		if (importObject instanceof Persistable)
			importMaps = (Persistable) importObject;
		else
			importMaps = (Persistable) DataSourceManager.getRootObject().set(GlobalData.IMPORT_MAPS, JsponicRuntime.newObject());
		System.err.println("importMaps " + importMaps);
		String webInfLocation =GlobalData.getWebInfLocation();		
		webInfLocation = webInfLocation.substring("file:".length());
		if (webInfLocation.startsWith("/C:"))
			webInfLocation = webInfLocation.substring(1);
		File upgradeDataFolder= new File(webInfLocation + 
						File.separatorChar + "upgrade" + File.separatorChar + "data");
		System.err.println("upgrade data folder is " + upgradeDataFolder);
		List<File> upgradeDataFiles = Arrays.asList(upgradeDataFolder.listFiles(new FilenameFilter() {
			public boolean accept(File dir, String name) {
				return name.indexOf('.') != -1 && "json".equals(name.substring(name.lastIndexOf('.')+1,name.length()));
			}
		}));
		Collections.sort(upgradeDataFiles,new Comparator() { // sort them by name
			public int compare(Object o1, Object o2) {
				return ((File)o1).getName().compareTo(((File)o1).getName());
			}
		});
		
		try {
			for (File upgradeDataFile : upgradeDataFiles) {
				String filename = upgradeDataFile.getName();
				System.err.println("upgradeDataFile " + upgradeDataFile);
				if (!Boolean.TRUE.equals(importMaps.get(filename))) {
					FileInputStream fs = new FileInputStream(upgradeDataFile);
					byte[] bytes = new byte[fs.available()];
					fs.read(bytes);
					JSONObject rootUpgrade = new JSONObject(new String(bytes));
					JSONArray data = rootUpgrade.getJSONArray("data");
					String source = rootUpgrade.getString("source");
					importObject = importMaps.get(source);		
					if (importObject instanceof Persistable)
						importMap = (Persistable) importObject;
					else {
						importMap = (Persistable) importMaps.set(source, JsponicRuntime.newObject());
						importMap.set("0", DataSourceManager.getRootObject());
					}
					for (int i = 0; i < data.length(); i++) {
						importData(data.getJSONObject(i));
					}
					//DataSourceManager.getRootObject().removeField(GlobalData.PARENT_FIELD);
					fs.close();
					importMaps.set(filename, true);
					//upgradeDataFile.renameTo(new File(upgradeDataFile.getPath()+ ".read"));
				}
			}
			//upgradeWebsitesForTemplates();
			//ensureDefaultWebsite();
		} catch (Exception e) {
			e.printStackTrace();
		} 
	}
/*	void upgradeVersions(DataObject editingVersions,boolean setCurrentVersion) {
		DataObject versionBasis = (DataObject) ((DataObject)DataSourceManager.getRootObject().get("templates")).get("versionBasis");
		if (setCurrentVersion)
			editingVersions.put("setCurrentVersion",versionBasis.get("setCurrentVersion"));
		DataObject fields = (DataObject) editingVersions.put("fields",  new TransientDataObjectList());
		fields.put("basis",versionBasis.get("fields"));
		if (setCurrentVersion) {
			String itemsId = ((DataObject) ((DataObject) versionBasis.get("fields")).get("currentVersion")).getId().toString();
			DataObject items = (DataObject) ((DataObject)fields.put("po$childMods", new TransientDataObjectList())).put(itemsId,new TransientDataObjectList());
			items.put("options",editingVersions);
		}
		
	}*/

	Persistable versioningBasis;
	boolean makeVersion(Persistable object, int requiredDepth) {
		while (!(object.get(GlobalData.BASIS_FIELD) ==Scriptable.NOT_FOUND)) {
			requiredDepth--;
			object = (Persistable) object.get(GlobalData.BASIS_FIELD) ;
		}
		return requiredDepth > 0;
	}
	void upgradeVersions(Persistable editingVersions) {
		Persistable versionsBasis = (Persistable) ((Persistable)DataSourceManager.getRootObject().get("templates")).get("versionsBasis");
		editingVersions.set("basis",versionsBasis);
	}
	void upgradeWebsite(Persistable website) { 
		try {
			System.err.println("upgrading site " + website.get("name"));
/*			DataObject settings = (DataObject) website.get("settings");
			if (settings.get("userStyles") instanceof DataNotFoundInstance) {
				DataObject defaultContentForStyles = (DataObject) ((DataObject) ((DataObject) ((DataObject) ((DataObject) settings.get("tools")).get("basis")).get("richtextToolbar")).get("formatting")).get("defaultContent");
				DataObject tools = (DataObject) settings.get("tools");
				DataObject defaultOptions = (DataObject) defaultContentForStyles.get("options");
				tools.put("stylesContent",new TransientDataObjectList());
				DataObject contentForStyles = (DataObject) tools.get("stylesContent");
				
				
				contentForStyles.put("basis", defaultContentForStyles);
				contentForStyles.put("options", new TransientDataObjectList());
				DataObject options = (DataObject) contentForStyles.get("options");
				options.put("basis", defaultOptions);
				settings.put("userStyles", options);
			}
			Persistable content = (Persistable) website.get("content");
			Persistable structure= (Persistable) content.get("structure");
			Persistable items = (Persistable) structure.get("items");
			items.set("type", ObjectId.idForString("js/Page"));
			content = (Persistable) content.get("allPages");
			structure= (Persistable) content.get("structure");
			items = (Persistable) structure.get("items");
			items.set("type", ObjectId.idForString("js/Page"));
			website.delete("structure");
			Persistable groupBasis = (Persistable) ((List) DataSourceManager.getRootObject().get("javaClassBasises")).get(0);
			
			List<Persistable> groups= (List) website.get("groups");
			for (Persistable group: groups) {
				try {
					if (group.get(GlobalData.BASIS_FIELD) != groupBasis)
						group.set(GlobalData.BASIS_FIELD, groupBasis);
				}
				catch(Exception e) {
					e.printStackTrace();
				}
			}
/*					if (((DataObject) user.get("userData")).get("inbox") instanceof DataNotFoundInstance) {
						((DataObject) ((DataObject) user.get("userData")).put("inbox", new TransientDataObjectList())).put(GlobalData.CONTAINS_REPEATING_IDENTIFIER,true);
					}
*				DataObject editingVersions = (DataObject) ((DataObject) user.get("userData")).get("editingVersions");
					if (editingVersions.get("setCurrentVersion") instanceof DataNotFoundInstance){
						upgradeVersions(editingVersions);
					}
				}
				catch(Exception e) {
					e.printStackTrace();
				}
			}
		/*	DataObjectList templates = (DataObjectList) website.get("templates");
			
			DataObjectList templatesBasis = (DataObjectList) (templates.get(GlobalData.CONTAINS_REPEATING_IDENTIFIER) instanceof DataNotFoundInstance ?
						templates.get(GlobalData.TEMPLATE_FIELD) : templates);
			DataObject pageBasis = (DataObject) templatesBasis.get(0);
			if (pageBasis.get(GlobalData.TEMPLATE_FIELD) instanceof DataNotFoundInstance) {
				templates.set(0, new TransientDataObject());
				((DataObject)templates.get(0)).put(GlobalData.TEMPLATE_FIELD, pageBasis);
				pageBasis =(DataObject) templates.get(0);
			}
			int i = 0;
			for (Object template: templatesBasis) {
				if (i++ > 0)
					if (((DataObject) template).get(GlobalData.TEMPLATE_FIELD) != pageBasis)
						((DataObject) template).put(GlobalData.TEMPLATE_FIELD,pageBasis);					
			}
			((DataObject)((DataObject)templates.get("fields")).get("items")).put("type",pageBasis);*/
		}
		catch(Exception e) {
			e.printStackTrace();
		}
	}
	void upgradeWebsitesForTemplates() {
		Persistable websites = (Persistable) DataSourceManager.getRootObject().get("websites");
		//Object fieldBasis = ((DataObject) DataSourceManager.getRootObject().get("templates")).get("fieldBasis");
		List<Persistable> javaClassBasises = (List<Persistable>) DataSourceManager.getRootObject().get("javaClassBasises");
		versioningBasis = javaClassBasises.get(0);
		//System.err.println("websites " + websites.size());
		for (String websiteName : websites.keySet()) {
			if (websites.get(websiteName) instanceof Persistable)
			upgradeWebsite((Persistable) websites.get(websiteName));
/*				DataObject fields = (DataObject) website.get("fields");
				if (fields.get("newInstance") instanceof DataNotFoundInstance) {
					DataObject newInstanceField = (DataObject) fields.put("newInstance",  new TransientDataObjectList());
					newInstanceField.put("advancedness",3);
					newInstanceField.put("name","newInstance");
					newInstanceField.put("basis",fieldBasis);
				}
				DataObject layout = (DataObject) website.get("layout");
				layout.removeField("fields");*/
				//DataObject settings = (DataObject) website.get("settings");
				/*settings.put("debugMode", false);
				settings.removeField("loginComponent");
				settings.removeField("shoppingCart");*/
				//if (settings.get("tools") instanceof DataNotFoundInstance) {
				//	DataObject tools = (DataObject) settings.put("tools", new TransientDataObjectList());
					//tools.put("basis",((DataObject) DataSourceManager.getRootObject().get("templates")).get("toolsBasis"));
				//}
		}
		//changeDescendant(null,DataSourceManager.getRootObject(),null,true);
		//changeBasises();
	}
	public static final String DATE_HEADER = "date:";
    Set<String> visited = new HashSet();
    Set<Persistable> needsNewBasis = new HashSet();
	Object importObject(Object object) throws JSONException{
		if (object instanceof JSONObject)
			return importData((JSONObject) object);
		if (object instanceof Integer) 
			object = new Long((Integer) object);
        if (object instanceof String && ((String)object).startsWith(DATE_HEADER)) { 
            return new Date(new Long(((String) object).substring(DATE_HEADER.length())).longValue());
        }

		if (object != null && object.equals(null))
			return null;
		return object;
	}
	Persistable getTarget(JSONObject object) throws JSONException {
		if (!object.has("id"))
			return null;
		String id = (String) object.remove("id");
		return getForId(id,object.has("array"));
	}
	Persistable getForId(String id, boolean isList) throws JSONException {
		if (id.startsWith("dyna/"))
			id = id.substring(5);
		try {
			// if it parseable we will create a new object
			Object targetObject = importMap.get(Integer.parseInt(id),importMap);
			if (targetObject instanceof Persistable) 
				return (Persistable) targetObject;
			return (Persistable) importMap.set(id, isList ? JsponicRuntime.newArray() : JsponicRuntime.newObject());
		}
		catch (NumberFormatException e) {
			return (Persistable) DataSourceManager.getById(id.substring(3));
		}
	}
	Persistable importData(JSONObject object) throws JSONException {
		try {
			Persistable target = getTarget(object);
			if (target == null)
				return null;
			Iterator keys = object.keys();
			while (keys.hasNext()) {
				try {
					String key = (String) keys.next();
					if (key.equals("array")) {
						if (object.get(key) instanceof JSONArray) {
							try {
								((PersistableList)target).clear();
							}
							catch (NoSuchElementException e) {
								System.err.println(e.getMessage());
							}
							JSONArray array = object.getJSONArray(key);
							for (int i = 0; i < array.length(); i++)
								((PersistableList)target).add(importObject(array.get(i)));
						}
					}
					else if (key.equals("po$childMods")) {
						target.set(key,importChildMods(object.getJSONObject(key)));
					}
					else {
						Object value = object.get(key);
						try {
							Integer.parseInt(key); // just in case we are in the middle of child mods import
							key = getForId(key,false).getId().toString();
						} catch (NumberFormatException e) {					
						}
						if (key.startsWith(GlobalData.ID_FIELD_PREFIX))
							key = GlobalData.ID_FIELD_PREFIX + getForId(key.substring(GlobalData.ID_FIELD_PREFIX.length()),false).getId();
						
						target.set(key,importObject(value));
					}
				} catch (RuntimeException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			return target;
		} catch (RuntimeException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return null;
		}
	}
	
	Persistable importChildMods(JSONObject object) throws JSONException {
		Persistable target = getTarget(object);
		if (target == null)
			return null;
		Iterator keys = object.keys();
		while (keys.hasNext()) {
			String key = (String) keys.next();
			String newKey = key;
			if (!key.equals("parent")) 
			{
				Object alteration = importMap.get(key);
				if (alteration instanceof Persistable) 
					newKey = ((Persistable) alteration).getId().toString();
				else 
					newKey = ((Persistable) importMap.set(key, JsponicRuntime.newObject())).getId().toString();
			}
			target.set(newKey,importObject(object.get(key)));			

		}
		return target;
	}
	

}
